package com.turing.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.turing.client.AppTemplateClient;
import com.turing.common.response.ResponseCode;
import com.turing.common.response.ResponseMessage;
import com.turing.common.response.ResponseVO;
import com.turing.common.util.JwtUtil;
import com.turing.common.util.ResponseUtil;
import com.turing.common.util.StringUtils;
import com.turing.common.util.Tools;
import com.turing.config.AdminConfig;
import com.turing.redis.common.RedisService;
import com.turing.user.dao.UserMapper;
import com.turing.user.entity.*;
import com.turing.user.service.*;
import com.turing.user.utils.DESUtils;
import com.turing.user.utils.TokenUtils;
import com.turing.user.vo.*;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.lang.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 服务类实现
 * @Author livin
 * @Date 2022-02-22
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private static final Log logger = LogFactory.getLog(UserServiceImpl.class);

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RoleService roleService;

    @Autowired
    private UserService userService;

    @Autowired
    private AdminConfig adminConfig;

    @Autowired
    private RoleUserService roleUserService;

    @Autowired
    private CustomerService customerService;

    @Autowired
    private RedisService<String, String> redisService;

    @Autowired
    private RoleFunctionPrivilegeService roleFunctionPrivilegeService;

    @Autowired
    private RoleTemplatePrivilegeService roleTemplatePrivilegeService;

    @Autowired
    private FunctionPrivilegeService functionPrivilegeService;

    @Autowired
    private AppTemplateClient appTemplateClient;

    @Autowired
    private HttpServletRequest request;

    @Override
    public ResponseVO<Object> login(String account, String password) {

        //如果是超管则特殊处理
        if(!StringUtils.isNull(account,password) && adminConfig.getAccount().equals(account) && adminConfig.getPassword().equals(password)){
            return ResponseUtil.success(adminLogin(adminConfig));
        }

        User user = null;
        if (Tools.isNotEmpty(account, password)) {
            user = new LambdaQueryChainWrapper<>(baseMapper).eq(User::getAccount, account).eq(User::getPassword, password).last("limit 1").one();
        }

        //账号密码是否存在
        if (user == null) {
            return ResponseUtil.failed(ResponseCode.LOGIN_ERROR, ResponseMessage.LOGIN_ERROR);
        }

        //账号是否禁用
        if (Integer.parseInt(user.getStatus()) != 0 || getDays() > 31) {
            return ResponseUtil.failed(ResponseCode.ACCOUNT_DISABLE, ResponseMessage.ACCOUNT_DISABLE);
        }

        //用户所属机构是否存在
        if (!userService.hasCustomerByAccount(account, password)) {
            return ResponseUtil.failed(ResponseCode.CUSTOMER_ERROR, ResponseMessage.CUSTOMER_ERROR);
        }

        Integer userId = user.getId();
        String userName = user.getName();
        LoginVO loginVo = generateLoginVo(account, userId, userName, null, null, user.getCustomerId(), user.getCustomerName());
        if(loginVo == null){
            return ResponseUtil.failed(ResponseCode.ROLE_NOT_SET,ResponseMessage.ROLE_NOT_SET);
        }

        logger.info(account+"账号登录系统成功!");
        return ResponseUtil.success(loginVo);
    }

    private LoginVO generateLoginVo(String account,Integer userId,String userName,Integer mainRoleId,String mainRoleName,
                                    Integer customerId,String customerName){

        LoginVO loginVO = new LoginVO();
        LoginTokenVO loginTokenVO = new LoginTokenVO();

        //获取用户所有角色id
        List<Integer> roleIds = roleUserService.getRoleIdsByUserId(userId);
        List<String> roleNames;
        if (Tools.isNotEmpty(roleIds)) {
            //获取用户所有角色名称
            roleNames = roleIds.stream().map(roleService::getById).map(Role::getName).collect(Collectors.toList());
        }else {
            return null;
        }

        if(StringUtils.isNull(mainRoleId,mainRoleName)) {
            mainRoleId = Tools.isEmpty(roleIds) ? null : roleIds.get(0);
            mainRoleName = Tools.isEmpty(roleNames) ? "" : roleNames.get(0);
        }

        loginTokenVO.setMainRoleId(mainRoleId);
        loginTokenVO.setMainRoleName(mainRoleName);
        loginTokenVO.setRoleIds(roleIds);
        loginTokenVO.setRoleNames(roleNames);
        loginTokenVO.setUserId(userId);
        loginTokenVO.setUserName(userName);
        loginTokenVO.setCustomerId(customerId);
        loginTokenVO.setCustomerName(customerName);

        //登录用户当前角色所有权限
        Set<RoleFunctionPrivilege> rfpCol = new HashSet<>();

        Role role = roleService.getById(mainRoleId);
        String dataPrivilege = null;
        if (role != null) {
            dataPrivilege = role.getClientPermissions();
        }

        List<Integer> roleUserIds = roleUserService.getUserIdsByRoleId(mainRoleId);

        //数据权限
        List<Integer> dataAll = roleService.getRoleIdsByDataPrivileges("all");
        List<Integer> dataRoleIds = dataAll != null ? dataAll : new ArrayList<>(Arrays.asList(2));

        Map<String, List<Integer>> dataPrivileges = new HashMap<>();
        //个人可见
        if ("personal".equals(dataPrivilege)) {
            dataPrivileges.put("able", Collections.singletonList(userId));
            dataPrivileges.put("dataRoleIds", dataRoleIds);
            //角色可见
        } else if ("role".equals(dataPrivilege)) {
            dataRoleIds.add(mainRoleId);
            dataPrivileges.put("dataRoleIds", dataRoleIds);
            //所有人可见,不包括超管
        } else if ("all".equals(dataPrivilege)) {
            dataPrivileges.put("disable", Collections.singletonList(adminConfig.getUserId()));
            dataPrivileges.put("dataRoleIds", dataRoleIds);
        }
        logger.info("非超管的数据权限:"+dataPrivileges);

        //构建token
        String token = generateToken(account, userId, userName, mainRoleId, mainRoleName, rfpCol, roleIds.toString(),
                roleNames.toString(), roleUserIds,dataPrivileges);
        loginTokenVO.setToken(token);
        loginTokenVO.setExpiredTime(TokenUtils.getExpiredTime().toString());

        long ttl = jwtUtil.getTtl() / 1000;
        //设置key并且添加过期时间
        redisService.set("CLLoginUser_" + userName + "_userId_" + userId, loginTokenVO.toString(), ttl);

        // build privilege tree
        List<LoginVO.Menu> menuList = new ArrayList<>();
        if (role != null && role.getStatus() == 0) {
            menuList = roleFunctionPrivilegeService.buildMenuTree(rfpCol);
        }
        loginVO.setMenuList(menuList);
        loginVO.setLoginTokenVO(loginTokenVO);

        return loginVO;
    }

    /**
     * 超管账号登录
     */
    private LoginVO adminLogin(AdminConfig adminConfig) {

        LoginVO loginVO = new LoginVO();
        LoginTokenVO loginTokenVO = new LoginTokenVO();

        Integer userId = adminConfig.getUserId();
        String userName = adminConfig.getUserName();
        Integer roleId = adminConfig.getRoleId();
        String roleName = adminConfig.getRoleName();
        Integer customerId = adminConfig.getCustomerId();
        String customerName = adminConfig.getCustomerName();

        loginTokenVO.setUserName(userName);
        loginTokenVO.setMainRoleId(roleId);
        loginTokenVO.setMainRoleName(roleName);
        loginTokenVO.setRoleIds(Collections.singletonList(roleId));
        loginTokenVO.setRoleNames(Collections.singletonList(roleName));
        loginTokenVO.setUserId(userId);
        loginTokenVO.setUserName(userName);
        loginTokenVO.setCustomerId(customerId);
        loginTokenVO.setCustomerName(customerName);

        Set<FunctionPrivilege> rfpCol = new HashSet<>();
        //构建token
        String token = generateAdminToken(adminConfig.getAccount(), userId, userName,roleId, roleName, rfpCol);
        loginTokenVO.setToken(token);
        loginTokenVO.setExpiredTime(TokenUtils.getExpiredTime().toString());

        long ttl = jwtUtil.getTtl() / 1000;
        //设置key并且添加过期时间
        redisService.set("CLLoginUser_" + userName + "_userId_" + userId, loginTokenVO.toString(), ttl);

        // build privilege tree
        List<LoginVO.Menu> menuList = functionPrivilegeService.buildMenuTree();
        loginVO.setMenuList(menuList);
        loginVO.setLoginTokenVO(loginTokenVO);

        return loginVO;
    }

    private String generateAdminToken(String account, Integer userId, String userName, Integer roleId, String roleName,
                                      Set<FunctionPrivilege> privCol) {

        Set<Integer> privilegeIds = new HashSet<>();
        List<FunctionPrivilege> privileges = functionPrivilegeService.getAllPrivilege();
        Set<Integer> privCols = privileges.stream().map(FunctionPrivilege::getId).collect(Collectors.toSet());
        privilegeIds.addAll(privCols);
        privCol.addAll(privileges);

        //通用文本比对场景权限
        Set<String> compareTemplates = new HashSet<>();
        compareTemplates.add("1");

        //获取所有要素抽取场景权限
        List<TemplateTO> extractTemplate = appTemplateClient.getAllTemplateByName(null);
        Map<String, Integer> fullTemplates = extractTemplate.stream().collect(Collectors.toMap(TemplateTO::getTemplateSn, a -> 0));
        Map<String, Integer> templates = new HashMap<>(fullTemplates);

        //数据权限所有人，包括超管
        Map<String,List<Integer>> dataPrivileges = new HashMap<>();
        dataPrivileges.put("all",new ArrayList<>());
        logger.info("超管的数据权限:"+dataPrivileges);
        return jwtUtil.createJWT(userId, account, userName, roleId, roleName, String.valueOf(roleId), roleName,
                new ArrayList<>(), new ArrayList<>(privilegeIds), templates, new HashMap<>(), compareTemplates, null, dataPrivileges);
    }

    private String generateToken(String account, Integer userId, String userName, Integer mainRoleId, String mainRoleName,
                                 Set<RoleFunctionPrivilege> rfpCol, String roleIds, String roleNames,
                                 List<Integer> roleUserIds,Map<String,List<Integer>> dataPrivileges) {
        Set<Integer> fps = new HashSet<>();
        List<RoleFunctionPrivilege> rfps = roleFunctionPrivilegeService.getPrivilegeByRole(mainRoleId);
        if (Tools.isNotEmpty(rfps)) {
            Set<Integer> privCol = rfps.stream().map(RoleFunctionPrivilege::getPrivilegeId).collect(Collectors.toSet());
            fps.addAll(privCol);
            rfpCol.addAll(rfps);
        }
        List<RoleTemplatePrivilegeVo> rtpVos = roleTemplatePrivilegeService.getPrivilegeByRole(mainRoleId);
        Map<String, Integer> templates = new HashMap<>();
        Set<String> compareTemplates = new HashSet<>();
        Set<String> ocrTemplates = new HashSet<>();
        Map<String, Set<Integer>> elements = new HashMap<>();
        if (Tools.isNotEmpty(rtpVos)) {
            Map<String, Integer> fullTemplates = rtpVos.stream().filter(rtp -> rtp.getPrivilegeType().equals(0)).collect(Collectors.toMap(RoleTemplatePrivilegeVo::getTemplateId, a -> 0));

            Map<String, Integer> notFullTemplateIds = rtpVos.stream().filter(rtp -> rtp.getPrivilegeType().equals(1)).collect(Collectors.toMap(RoleTemplatePrivilegeVo::getTemplateId, a -> 1, (o1, o2) -> o1));

            compareTemplates = rtpVos.stream().filter(rtp -> rtp.getPrivilegeType().equals(3)).map(RoleTemplatePrivilegeVo::getTemplateId).collect(Collectors.toSet());

            ocrTemplates = rtpVos.stream().filter(rtp -> rtp.getPrivilegeType().equals(4)).map(RoleTemplatePrivilegeVo::getTemplateId).collect(Collectors.toSet());

            templates.putAll(fullTemplates);
            templates.putAll(notFullTemplateIds);
            elements = rtpVos.stream().filter(rtp -> rtp.getPrivilegeType().equals(1)).collect(Collectors.groupingBy(RoleTemplatePrivilegeVo::getTemplateId, Collectors.mapping(RoleTemplatePrivilegeVo::getElementId, Collectors.toSet())));
        }
        return jwtUtil.createJWT(userId, account, userName, mainRoleId, mainRoleName, roleIds, roleNames, roleUserIds,
                new ArrayList<>(fps), templates, elements, compareTemplates, ocrTemplates, dataPrivileges);
    }

    @Override
    public String loginOut(String token) {

        Claims claims = jwtUtil.parseJWT(token);
        String id = String.valueOf(claims.get("jti"));
        String userName = String.valueOf(claims.get("sub"));
        String xpLoginKey = "CLLoginUser_" + userName + "_userId_" + id;
        String hasLogin = redisService.get(xpLoginKey);
        if (hasLogin != null) {
            redisService.delete(xpLoginKey);
        }

        return ResponseCode.SUCCESS;
    }

    @Override
    public boolean CheckUserPrivilegeByUserId(Integer userId, Integer mainRoleId, String url) {
        List<RoleFunctionPrivilege> privilegeByRole = roleFunctionPrivilegeService.getPrivilegeByRole(mainRoleId);
        if (Tools.isNotEmpty(privilegeByRole)) {
            List<Integer> privilegeIds = privilegeByRole.stream().map(RoleFunctionPrivilege::getPrivilegeId).collect(Collectors.toList());
            return functionPrivilegeService.checkUserPrivilege(privilegeIds, url);
        }
        return false;

    }

    @Override
    public LoginVO changeMainRole(Integer roleId) {

        LoginVO loginVO = new LoginVO();

        String token = request.getHeader("token");
        Claims claims = jwtUtil.parseJWT(token);
        String userId = String.valueOf(claims.get("jti"));
        String userName = String.valueOf(claims.get("sub"));
        String account = String.valueOf(claims.get("userCode"));
        String xpLoginKey = "CLLoginUser_" + userName + "_userId_" + userId;
        String hasLogin = redisService.get(xpLoginKey);

        if (hasLogin != null) {

            //如果是超管则特殊处理
            if(!StringUtils.isNull(account) && adminConfig.getAccount().equals(account)){
                return adminLogin(adminConfig);
            }

            Role role = roleService.getById(roleId);
            String roleName = role.getName();
            User user = getById(userId);
            loginVO = generateLoginVo(account, Integer.valueOf(userId), userName, roleId, roleName,
                    user.getCustomerId(), user.getCustomerName());
        }
        return loginVO;

    }

    @Override
    public UserVO queryOne(Integer userId) {
        UserVO userVO = new UserVO();
        User user = getById(userId);
        Optional.ofNullable(user).ifPresent(u -> BeanUtils.copyProperties(u, userVO));
        return userVO;
    }

    @Override
    public Integer getUserStatusById(Integer userId) {
        if(adminConfig.getUserId().equals(userId)){
            return 0;
        }
        String status = baseMapper.selectById(userId).getStatus();
        return Integer.valueOf(status);
    }

    @Override
    public User getUserByAccount(String account, String password) {
        return new LambdaQueryChainWrapper<>(baseMapper).eq(User::getAccount, account).eq(User::getPassword, password).last("limit 1").one();
    }

    @Override
    public boolean hasCustomerByAccount(String account, String password) {
        User user = getUserByAccount(account, password);
        if (user != null && user.getCustomerId() != null) {
            Customer customer = customerService.getById(user.getCustomerId());
            return customer != null && customer.getStatus() == 0;
        }
        return false;
    }

    @Override
    public ResponseVO<Object> skip2Login(String account) {
        try {
            logger.info("account:" + account);
            String accountName = DESUtils.decrypt(account);
            Assert.isTrue(!"000000".equals(accountName), "权限不足");
            //save user
            User user = userService.getUserByAccountName(accountName);
            if (user == null) {
                user = userService.saveUser(accountName);
            }
            //save roleUser
            boolean flag = roleUserService.getRoleIdByUserId(user.getId());
            if (flag) {
                roleUserService.save(RoleUser.builder().roleId(293).userId(user.getId()).build());
            }
            return login(accountName, user.getPassword());
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
        return new ResponseVO<>("405", "权限不足", -1);
    }

    @Override
    public User getUserByAccountName(String accountName) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getAccount, accountName);
        List<User> users = baseMapper.selectList(queryWrapper);
        return Tools.isNotEmpty(users) ? users.get(0) : null;
    }

    @Override
    public User saveUser(String accountName) {
        User user = User.builder().name(accountName).account(accountName).password(StringUtils.getSign("12xy5@6")).gender("男")
                .certificateType("身份证").loginType("账号密码").status("0").customerName(accountName)
                .customerId(256).createdTime(LocalDateTime.now()).updatedTime(LocalDateTime.now()).build();
        save(user);
        return user;
    }

    public int getDays() {
        String dateStr = "2023-02-28 00:00:01";
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        int days = 50;
        try {
            Date date = new Date();
            Date date2 = format.parse(dateStr);
            days = (int) ((date.getTime() - date2.getTime()) / (1000*3600*24));
        }catch (Exception e){
            logger.info("=====>getDays()方法，日期转换错误：");
            e.printStackTrace();
        }
        return days;

    }
}
